/*
 * @Author: pardon110
 * @Date: 2024-03-18 11:30:20
 * @LastEditors: pardon110@qq.com
 * @LastEditTime: 2024-03-18 16:14:59
 * @FilePath: \days7\web\template\ssr\engine.go
 * @Description:
 */
package ssr

import (
	"html/template"
	"log"
	"net/http"
	"path"
	"strings"
)

type HandlerFunc func(*Context)

type (
	RouterGroup struct {
		prefix      string
		middlewares []HandlerFunc
		engine      *Engine // share a engine instance
	}

	// share a engine instance
	Engine struct {
		*RouterGroup
		*router
		groups        []*RouterGroup     // store all groups
		htmlTemplates *template.Template // for html render global
		funcMap       template.FuncMap   // for html render
	}
)

// all groups share the same engine instance
func New() *Engine {
	e := &Engine{router: newRouter()}
	e.RouterGroup = &RouterGroup{engine: e}
	e.groups = []*RouterGroup{e.RouterGroup}
	return e
}

func Default() *Engine {
	e := New()
	e.Use(Logger(), Recovery())
	return e
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var middlewares []HandlerFunc
	for _, group := range e.groups {
		if strings.HasPrefix(r.URL.Path, group.prefix) { // 通过请求path筛选对应的中间件
			middlewares = append(middlewares, group.middlewares...)
		}
	}
	c := newContext(w, r)
	c.handlers = middlewares // 上下文绑定中间件
	c.engine = e             // 上下文绑定引擎，间接访问模板
	e.router.handle(c)       // 委托router实例，间接调用中间件及端点路由逻辑函数
}

func (e *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, e)
}

// render function
func (e *Engine) SetFuncMap(funcMap template.FuncMap) {
	e.funcMap = funcMap
}

func (e *Engine) LoadHTMLGlob(pattern string) {
	// 多模板文件加载
	e.htmlTemplates = template.Must(template.New("").Funcs(e.funcMap).ParseGlob(pattern))
}

func (g *RouterGroup) Group(prefix string) *RouterGroup {
	e := g.engine
	newGroup := &RouterGroup{
		engine: e,
		prefix: g.prefix + prefix,
	}
	e.groups = append(e.groups, newGroup)
	return newGroup
}

func (g *RouterGroup) Use(middleware ...HandlerFunc) {
	g.middlewares = append(g.middlewares, middleware...)
}

func (g *RouterGroup) addRoute(method string, comp string, handler HandlerFunc) {
	pattern := g.prefix + comp
	log.Printf("Route %4s - %s", method, pattern)
	g.engine.router.addRoute(method, pattern, handler)
}

func (g *RouterGroup) GET(pattern string, handler HandlerFunc) {
	g.addRoute("GET", pattern, handler)
}

// POST defines the method to add POST request
func (g *RouterGroup) POST(pattern string, handler HandlerFunc) {
	g.addRoute("POST", pattern, handler)
}

// create static handler
// 形成一个闭包
func (g *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
	absolutePath := path.Join(g.prefix, relativePath)
	// func http.FileServer(root http.FileSystem) http.Handler  创建一个文件静态服务器
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
	return func(c *Context) {
		file := c.Param("filepath")
		if _, err := fs.Open(file); err != nil {
			c.Status(http.StatusNotFound)
			return
		}
		// origin http
		fileServer.ServeHTTP(c.Writer, c.Req)
	}
}

// Static export serve static files
func (g *RouterGroup) Static(relativePath string, root string) {
	handler := g.createStaticHandler(relativePath, http.Dir(root))
	urlPattern := path.Join(relativePath, "/*filepath")

	// Register GET handlers
	g.GET(urlPattern, handler)
}
